#include "cPC.h"
#include "cPath.h"
#include "cScene.h"
#include "cPoint2D.h"
#include <cstdlib>
#include <cmath>

#define FM_PI (3.1415927f)

void cPC::SelectAttackCell(cScene *scene, int destcx, int destcy, int npcCx, int npcCy)
{
	/* int offsetx, offsety, start, now, newDestcx, newDestcy;
	// First check if the starting tile is walkable
	GoToCell(scene, destcx, destcy);
	// Path is ok, tile is walkable. DONE
	if (path.nxf >= 0) return;

	// Tile is not walkable so choone another one
	// Surrounding tile schema:
	// 3 2 1
	// 4 X 0
	// 5 6 7

	// Check the tile offset between npc and choosen tile
	offsetx = destcx - npcCx;
	offsety = destcy - npcCy;
	if(offsetx == 1 && offsety == 0)		start = 0;
	else if(offsetx == 1 && offsety == 1)	start = 1;
	else if(offsetx == 0 && offsety == 1)	start = 2;
	else if(offsetx == -1 && offsety == 1)	start = 3;
	else if(offsetx == -1 && offsety == 0)	start = 4;
	else if(offsetx == -1 && offsety == -1)	start = 5;
	else if(offsetx == 0 && offsety == -1)	start = 6;
	else									start = 7;

	// Circumvallate sorrounding tiles with -/+ alternating steps
	// From the tile num generate displacement
	for (int i = 1; i < 4; ++i)
	{
		now = (start - i) % 8;
		if(now == 0){		newDestcx = npcCx+1; newDestcy = npcCy; }
		else if(now == 1){	newDestcx = npcCx+1; newDestcy = npcCy-1; }
		else if(now == 2){	newDestcx = npcCx; newDestcy = npcCy-1; }
		else if(now == 3){	newDestcx = npcCx-1; newDestcy = npcCy-1; }
		else if(now == 4){	newDestcx = npcCx-1; newDestcy = npcCy; }
		else if(now == 5){	newDestcx = npcCx-1; newDestcy = npcCy+1; }
		else if(now == 6){	newDestcx = npcCx; newDestcy = npcCy+1; }
		else{				newDestcx = npcCx+1; newDestcy = npcCy+1; }
		GoToCell(scene, newDestcx, newDestcy);
		if (path.nxf >= 0) return;

		now = (start + i) % 8;
		if(now == 0){		newDestcx = npcCx+1; newDestcy = npcCy; }
		else if(now == 1){	newDestcx = npcCx+1; newDestcy = npcCy-1; }
		else if(now == 2){	newDestcx = npcCx; newDestcy = npcCy-1; }
		else if(now == 3){	newDestcx = npcCx-1; newDestcy = npcCy-1; }
		else if(now == 4){	newDestcx = npcCx-1; newDestcy = npcCy; }
		else if(now == 5){	newDestcx = npcCx-1; newDestcy = npcCy+1; }
		else if(now == 6){	newDestcx = npcCx; newDestcy = npcCy+1; }
		else{				newDestcx = npcCx+1; newDestcy = npcCy+1; }
		GoToCell(scene, newDestcx, newDestcy);
		if (path.nxf >= 0) return;
	}

	now = (start + 4) % 8; 	
	if(now == 0){		newDestcx = npcCx+1; newDestcy = npcCy; }
	else if(now == 1){	newDestcx = npcCx+1; newDestcy = npcCy-1; }
	else if(now == 2){	newDestcx = npcCx; newDestcy = npcCy-1; }
	else if(now == 3){	newDestcx = npcCx-1; newDestcy = npcCy-1; }
	else if(now == 4){	newDestcx = npcCx-1; newDestcy = npcCy; }
	else if(now == 5){	newDestcx = npcCx-1; newDestcy = npcCy+1; }
	else if(now == 6){	newDestcx = npcCx; newDestcy = npcCy+1; }
	else{				newDestcx = npcCx+1; newDestcy = npcCy+1; }
	GoToCell(scene, newDestcx, newDestcy);*/
}

cPC::cPC(void)
{
	cx = 0;
	cy = 0;
	x = 0;
	y = 0;

	animSeq = 0;
	animDelay = 0;

	goingToAttack = false;
	attack = false;
	attackSeq = 0;
	attackDelay = 0;
}

cPC::~cPC(void)
{
}

void cPC::GoToCell(cScene *scene, int destcx, int destcy)
{
	// "leave all we're doing"
	goingToAttack = false;
	attack = false;

	// Go
	if (path.IsDone() ) path.Make(scene, cx, cy, destcx, destcy);
	else path.ReMake(scene, destcx, destcy);
}

void cPC::GoToNPC(cScene *scene, cNPC *npc)
{
	/*int destcx, destcy, dirSector;
	cPoint2D *v_Direction;

	npc->GetCell(&destcx, &destcy);
	v_Direction = new cPoint2D(destcx-cx, destcy-cy);

	// Define [0, 7] 8 direction double-cones depending on wich is the exact direction angle. Select to attack the most natural cell (AKA the one in front of the player)
	dirSector = int(floor(8.0f * acos(float(v_Direction->X() ) / sqrt(pow(float(v_Direction->X() ), 2) + pow(float(v_Direction->Y() ), 2) ) ) / FM_PI) );

	// Define 8 direction cones depending on wich is the exact direction angle. Select to attack the most natural cell (AKA the one in front of the player)

	// X axis
	if(dirSector == 0 || dirSector == 7)
	{
		// -X
		if (v_Direction->X() < 0) SelectAttackCell(scene, destcx+1, destcy, destcx, destcy);
		// +X
		else SelectAttackCell(scene, destcx-1, destcy, destcx, destcy);
	}
	// Y axis
	else if (dirSector == 3 || dirSector == 4)
	{
		// -Y
		if (v_Direction->Y() < 0) SelectAttackCell(scene, destcx, destcy+1, destcx, destcy);
		// +Y
		else SelectAttackCell(scene, destcx, destcy-1, destcx, destcy);
	}
	// Diagonals
	else
	{
		// -X-Y
		if (v_Direction->X() < 0 && v_Direction->Y() < 0) SelectAttackCell(scene, destcx+1, destcy+1, destcx, destcy);
		// -X+Y
		else if (v_Direction->X() < 0 && v_Direction->Y() > 0) SelectAttackCell(scene, destcx+1, destcy-1, destcx, destcy);
		// +X-Y
		else if (v_Direction->X() > 0 && v_Direction->Y() < 0) SelectAttackCell(scene, destcx-1, destcy+1, destcx, destcy);
		//+X+Y
		else SelectAttackCell(scene, destcx-1, destcy-1, destcx, destcy);
	}
	delete v_Direction;

	if (npc->IsEnemy() )
	{
		goingToAttack = true;
		attack = false;
	}*/
}

void cPC::BlockCells(cScene *scene)
{
	int *walkabilityMap = NULL;	

	scene->GetWalkabilityMap(&walkabilityMap);

	--walkabilityMap[cy*scene->GetSceneArea() + cx];
	if(cy-1 >= 0 && cx-1 >= 0 && walkabilityMap[(cy-1)*scene->GetSceneArea() + cx-1] <= 0)
		--walkabilityMap[(cy-1)*scene->GetSceneArea() + cx-1];
	if(cx-1 >= 0 && walkabilityMap[cy*scene->GetSceneArea() + cx-1] <= 0)
		--walkabilityMap[cy*scene->GetSceneArea() + cx-1];
	if(cy+1 < scene->GetSceneArea() && cx-1 >= 0 && walkabilityMap[(cy+1)*scene->GetSceneArea() + cx-1] <= 0)
		--walkabilityMap[(cy+1)*scene->GetSceneArea() + cx-1];
	if(cy+1 < scene->GetSceneArea() && walkabilityMap[(cy+1)*scene->GetSceneArea() + cx] <= 0)
		--walkabilityMap[(cy+1)*scene->GetSceneArea() + cx];
	if(cy+1 < scene->GetSceneArea() && cx+1 < scene->GetSceneArea() && walkabilityMap[(cy+1)*scene->GetSceneArea() + cx+1] <= 0)
		--walkabilityMap[(cy+1)*scene->GetSceneArea() + cx+1];
	if(cx+1 < scene->GetSceneArea() && walkabilityMap[cy*scene->GetSceneArea() + cx+1] <= 0)
		--walkabilityMap[cy*scene->GetSceneArea() + cx+1];
	if(cy-1 >= 0 && cx+1 < scene->GetSceneArea() && walkabilityMap[(cy-1)*scene->GetSceneArea() + cx+1] <= 0)
		--walkabilityMap[(cy-1)*scene->GetSceneArea() + cx+1];
	if(cy-1 >= 0 && walkabilityMap[(cy-1)*scene->GetSceneArea() + cx] <= 0)
		--walkabilityMap[(cy-1)*scene->GetSceneArea() + cx];	
}

void cPC::UnBlockCells(cScene *scene)
{
	int *walkabilityMap = NULL;

	scene->GetWalkabilityMap(&walkabilityMap);

	++walkabilityMap[cy*scene->GetSceneArea() + cx];
	if(cy-1 >= 0 && cx-1 >= 0 && walkabilityMap[(cy-1)*scene->GetSceneArea() + cx-1] < 0)
		++walkabilityMap[(cy-1)*scene->GetSceneArea() + cx-1];
	if(cx-1 >= 0 && walkabilityMap[cy*scene->GetSceneArea() + cx-1] < 0)
		++walkabilityMap[cy*scene->GetSceneArea() + cx-1];
	if(cy+1 < scene->GetSceneArea() && cx-1 >= 0 && walkabilityMap[(cy+1)*scene->GetSceneArea() + cx-1] < 0)
		++walkabilityMap[(cy+1)*scene->GetSceneArea() + cx-1];
	if(cy+1 < scene->GetSceneArea() && walkabilityMap[(cy+1)*scene->GetSceneArea() + cx] < 0)
		++walkabilityMap[(cy+1)*scene->GetSceneArea() + cx];
	if(cy+1 < scene->GetSceneArea() && cx+1 < scene->GetSceneArea() && walkabilityMap[(cy+1)*scene->GetSceneArea() + cx+1] < 0)
		++walkabilityMap[(cy+1)*scene->GetSceneArea() + cx+1];
	if(cx+1 < scene->GetSceneArea() && walkabilityMap[cy*scene->GetSceneArea() + cx+1] < 0)
		++walkabilityMap[cy*scene->GetSceneArea() + cx+1];
	if(cy-1 >= 0 && cx+1 < scene->GetSceneArea() && walkabilityMap[(cy-1)*scene->GetSceneArea() + cx+1] < 0)
		++walkabilityMap[(cy-1)*scene->GetSceneArea() + cx+1];
	if(cy-1 >= 0 && walkabilityMap[(cy-1)*scene->GetSceneArea() + cx] < 0)
		++walkabilityMap[(cy-1)*scene->GetSceneArea() + cx];
}

void cPC::Move(cScene *scene)
{
	bool willCollide = false;
	int cxdest, cydest, move;
	int *walkabilityMap = NULL;

	scene->GetWalkabilityMap(&walkabilityMap);

	if(!path.IsDone() )
	{
		switch(path.Faced() )
		{
			case N:
				if(cy-1 >= 0 && walkabilityMap[(cy-1)*scene->GetSceneArea() + cx] < 0)
					willCollide = true;
				break;
			case S:
				if(cy+1 < scene->GetSceneArea() && walkabilityMap[(cy+1)*scene->GetSceneArea() + cx] < 0)
					willCollide = true;
				break;
			case E:
				if(cx+1 < scene->GetSceneArea() && walkabilityMap[cy*scene->GetSceneArea() + cx+1] < 0)
					willCollide = true;
				break;
			case O:
				if(cx-1 >= 0 && walkabilityMap[cy*scene->GetSceneArea() + cx-1] < 0)
					willCollide = true;
				break;
			case NE:
				if(cx+1 < scene->GetSceneArea() && cy-1 >= 0 && walkabilityMap[(cy-1)*scene->GetSceneArea() + cx+1] < 0)
					willCollide = true;
				break;
			case NO:
				if(cx-1 >= 0 && cy-1 >= 0 && walkabilityMap[(cy-1)*scene->GetSceneArea() + cx-1] < 0)
					willCollide = true;
				break;
			case SE:
				if(cx+1 < scene->GetSceneArea() && cy+1 < scene->GetSceneArea() && walkabilityMap[(cy+1)*scene->GetSceneArea() + cx+1] < 0)
					willCollide = true;
				break;
			case SO:
				if(cx-1 >= 0 && cy+1 < scene->GetSceneArea() && walkabilityMap[(cy+1)*scene->GetSceneArea() + cx-1] < 0)
					willCollide = true;
				break;
			default:
				return;
		}

		if(willCollide)
		{
			path.GetDestinationCell(&cxdest, &cydest);
			path.ReMake(scene, cxdest, cydest);
		}

		move = path.NextStep(&x, &y, &cx, &cy);

		if(move == ARRIVE)
		{
			path.Done();
			animSeq=0;
		}
	}
	else
	{
		//Moved for attack?
		if(goingToAttack)
		{
			attack = true;
			attackSeq = 0;
			attackDelay = 0;
			goingToAttack = false;
		}
	}
}

void cPC::SetPosition(int posx, int posy)
{
	x = posx;
	y = posy;
}

void cPC::GetPosition(int *posx, int *posy)
{
	*posx = x;
	*posy = y;
}

void cPC::SetCell(int cellx, int celly)
{
	cx = cellx;
	cy = celly;
}

void cPC::GetCell(int *cellx, int *celly)
{
	*cellx = cx;
	*celly = cy;
}

void cPC::GetRect(RECT *rc, int *posx, int *posy, cScene *scene)
{
	int sCx, sCy;
	scene->GetSceneOrigin(&sCx, &sCy);
	*posx = SCENE_Xo + x - (sCx<<5);
	*posy = SCENE_Yo + y - (sCy<<5);

	switch(path.Faced() )
	{
		case STOP:
			SetRect(rc, 128, 0, 160, 32);
			break;
		case N:
			SetRect(rc, animSeq<<5, 0, (animSeq+1)<<5, 32);
			break;
		case S:
			SetRect(rc, (4+animSeq)<<5, 0, (4+animSeq+1)<<5, 32);
			break;
		case SE:
		case NE:
		case E:
			SetRect(rc, (4+animSeq)<<5, 32, (4+animSeq+1)<<5, 64);
			break;
		case SO:
		case NO:
		case O:
			SetRect(rc, animSeq<<5, 32, (animSeq+1)<<5, 64);
			break;
	}
	if(!path.IsDone() )
	{
		++animDelay;
		if(animDelay == 4)
		{
			++animSeq;
			if(animSeq == 4) animSeq = 0;
			animDelay = 0;
		}
	}
}

void cPC::GetRectRadar(RECT *rc, int *posx, int *posy)
{
	*posx = RADAR_Xo + (cx<<2);
	*posy = RADAR_Yo + (cy<<2);

	SetRect(rc, 80, 32, 84, 36);
}

void cPC::GetRectLife(RECT *rc, int *posx, int *posy, cScene *scene)
{
	int sCx, sCy;
	scene->GetSceneOrigin(&sCx, &sCy);

	*posx = SCENE_Xo + x - (sCx<<5);
	*posy = SCENE_Yo + y - (sCy<<5);

	//Life dependency not implemented
	SetRect(rc, 0, 32, 32, 64);
}

void cPC::GetRectAttack(RECT *rc, int *posx, int *posy, cScene *scene)
{
	int sCx, sCy;
	scene->GetSceneOrigin(&sCx, &sCy);

	*posx = SCENE_Xo + x - (sCx<<5);
	*posy = SCENE_Yo + y - (sCy<<5);

	SetRect(rc, attackSeq<<5, 64, (attackSeq+1)<<5, 96);
	switch(attackSeq)
	{
		case 0:	*posx; break;
		case 1:	*posx-=8; break;
		case 2: *posx-=16; break;
		case 3: *posx-=24; break;
		//case 4: *posx-=24; break;
		//case 5: *posx-=32; break;
		//case 6: *posx-=32; break;
		//case 7: *posx-=32; break;
	}
	
	++attackDelay;
	if(attackDelay == 4)
	{
		++attackSeq;
		if(attackSeq == 4) attackSeq = 0;
		attackDelay = 0;
	}
}

bool cPC::IsGoingToAttack()
{
	return goingToAttack;
}

bool cPC::IsAttacking()
{
	return attack;
}